// Copyright XXX Inc.  All rights reserved.
// 许可证
// 作者：软件1902冯若轩

// 本软件提供自动生成中小学数学卷子的功能，生成的试卷保存在相应账号的文件夹下。
// 该文件是教师用户类的各成员函数及试卷生成相关函数的实现

#include "teacher.h"

#include <algorithm>
#include <cstdlib>
#include <string>

namespace paper {
namespace teacher {

using paper::database::Database;
using paper::io::GetQuestionsFromFile;
using paper::io::GetString;
using paper::io::GetUsernameAndPassword;
using paper::io::WritePaperIntoFile;
using paper::teacher::Teacher;
using std::list;
using std::pair;
using std::string;
using std::to_string;
using std::vector;

// 加减乘除基本操作符数组
const string operators_basic[4] = {"+", "-", "*", "/"};
// 开根号与开平方初中符号数组
const string operators_middle[2] = {"√", "^2"};
// sin、cos和tan高中符号数组
const string operators_high[3] = {"sin", "cos", "tan"};

// 生成num个大小范围为[min,max]的不重复整数，保存在一个list中
list<int> GenerateRandomList(int min, int max, int num) {
  list<int> result;
  while (result.size() < num) {
    result.push_back(rand() % (max - min + 1) + min);
    result.sort();
    result.unique();  // 每次只添加1个数，如果有重复，排序后一定是相邻的两个数
  }
  return result;
}

// 根据需要的类型type，生成不同的试卷。生成时将题目以操作数为基点拆分成组，
// 一个操作数及其前后括号位、前后自操作符号位和基本操作符位为一组
// 根据类型随机生成并逐一填充后，将每位上的子字符串拼接为完整的题目字符串
string MakeQuestion(char type) {
  int i, operand_num, random_number;
  // bracket_begin为括号起始位，bracket_end为括号终止位
  int bracket_begin = -1, bracket_end = -1;
  vector<string> result_pieces(200, "");  // 题目拆分为不同的位
  bool have_bracket = false;
  string result = "";  // 生成的题目字符串
  if (type == 'X') {
    // 小学试卷有2-5个操作数，随机生成操作数数量
    operand_num = rand() % 4 + 2;
    result_pieces.resize(4 * operand_num - 1, "");
    // 在除最后一个操作数外的每个操作数后随机添加基本操作符
    for (i = 0; i < operand_num - 1; i++) {
      random_number = rand() % 4;
      result_pieces[i * 4 + 3] = operators_basic[random_number];
    }
    // 随机生成操作数并插入
    for (i = 0; i < operand_num; i++) {
      random_number = rand() % 100 + 1;
      result_pieces[i * 4 + 1] = to_string(random_number);
    }
    // 操作数大于2的题目有50%的概率存在一个括号
    random_number = rand() % 2;
    if (random_number && operand_num > 2) {
      have_bracket = true;
      bracket_begin = rand() % (operand_num - 1);
      // 如果括号起始于第一个操作数前，那么就不应结尾在最后一个操作数后，否则无意义
      if (bracket_begin == 0) {
        bracket_end = rand() % (operand_num - 2) + 1;
      } else {
        bracket_end =
            rand() % (operand_num - 1 - bracket_begin) + bracket_begin + 1;
      }
      result_pieces[bracket_begin * 4] = "(";
      result_pieces[bracket_end * 4 + 2] = ")";
    }
  } else if (type == 'C') {
    list<int> operators_middle_index;  // 开平方和平方符号随机生成的位置
    list<int>::iterator iter;
    // 初中试卷有1-5个操作数，随机生成操作数数量
    operand_num = rand() % 5 + 1;
    result_pieces.resize(6 * operand_num - 1, "");
    // 在除最后一个操作数外的每个操作数后随机添加基本操作符
    for (i = 0; i < operand_num - 1; i++) {
      random_number = rand() % 4;
      result_pieces[i * 6 + 5] = operators_basic[random_number];
    }
    // 随机生成操作数并插入
    for (i = 0; i < operand_num; i++) {
      random_number = rand() % 100 + 1;
      result_pieces[i * 6 + 2] = to_string(random_number);
    }
    // 随机生成至少1个，至多操作数个初中操作符，每个操作数至多添加一个初中操作符
    // 每个添加位置上添加开平方和平方符号的概率各为50%
    operators_middle_index =
        GenerateRandomList(0, operand_num - 1, rand() % operand_num + 1);
    for (iter = operators_middle_index.begin();
         iter != operators_middle_index.end(); ++iter) {
      random_number = rand() % 2;
      if (random_number == 0) {
        result_pieces[(*iter) * 6 + 1] = operators_middle[random_number];
      } else {
        result_pieces[(*iter) * 6 + 3] = operators_middle[random_number];
      }
    }
  } else {
    list<int> operators_high_index;  // sin、cos和tan符号随机生成的位置
    list<int>::iterator iter;
    // 高中试卷有1-5个操作数，随机生成操作数数量
    operand_num = rand() % 5 + 1;
    result_pieces.resize(6 * operand_num - 1, "");
    // 在除最后一个操作数外的每个操作数后随机添加基本操作符
    for (i = 0; i < operand_num - 1; i++) {
      random_number = rand() % 4;
      result_pieces[i * 6 + 5] = operators_basic[random_number];
    }
    // 随机生成操作数并插入
    for (i = 0; i < operand_num; i++) {
      random_number = rand() % 100 + 1;
      result_pieces[i * 6 + 2] = to_string(random_number);
    }
    // 随机生成至少1个，至多操作数个高中操作符，每个操作数至多添加一个高中操作符
    // 每个添加位置上添加sin、cos或tan符号的概率各为1/3
    operators_high_index =
        GenerateRandomList(0, operand_num - 1, rand() % operand_num + 1);
    for (iter = operators_high_index.begin();
         iter != operators_high_index.end(); ++iter) {
      random_number = rand() % 3;
      result_pieces[(*iter) * 6 + 1] = operators_high[random_number];
    }
    // 在没有添加高中操作符的操作数上随机添加初中操作符，每个操作数的添加概率为50%，
    // 在确定添加时，添加开平方和平方符号的概率各为50%
    for (i = 0; i < operand_num; i++) {
      random_number = rand() % 2;
      if (result_pieces[i * 6 + 1] == "" && random_number == 1) {
        random_number = rand() % 2;
        if (random_number == 0) {
          result_pieces[i * 6 + 1] = operators_middle[random_number];
        } else {
          result_pieces[i * 6 + 3] = operators_middle[random_number];
        }
      }
    }
  }
  // 将各位上的子字符串拼接为完整的题目，并加上等于号
  for (i = 0; i < result_pieces.size(); i++) {
    result += result_pieces[i];
  }
  result += "=";
  return result;
}

// 创建时初始化各成员变量，已登录和请求退出标志均为false
Teacher::Teacher() {
  mode_ = 'N';
  name_ = "";
  mode_name_ = "NULL";
  already_login_ = false;
  request_logout_ = false;
}

// 读取用户输入的账号密码，并调用数据库的LoginCheck函数判断是否登录成功
// 若成功则根据函数返回值设置类型和类型名
// 若不成功则一直提示输入用户名和密码，直到登录成功或退出程序
void Teacher::Login(Database* all_user) {
  char login_status;
  pair<string, string> username_and_password;
  // 未登陆成功就反复提示登录
  while (!already_login_) {
    printf("\n\n请输入用户名和密码(使用空格隔开):");
    username_and_password = GetUsernameAndPassword();
    login_status = all_user->LoginCheck(username_and_password.first,
                                        username_and_password.second);
    if (login_status == 'N') {
      printf("\n\033[0;31;40m请输入正确的用户名、密码\033[0m\n");
    } else {
      system("cls");
      mode_ = login_status;
      name_ = username_and_password.first;
      switch (mode_) {
        case 'X':
          mode_name_ = "小学";
          break;

        case 'C':
          mode_name_ = "初中";
          break;

        case 'G':
          mode_name_ = "高中";
          break;

        default:
          break;
      }
      all_question_ = GetQuestionsFromFile(name_);
      already_login_ = true;
      AfterLogin();
      // 若登录后请求退出标志变为true，则进行退出前初始化并退出登录
      if (request_logout_) {
        LogoutInit();
        return;
      }
    }
  }
}

// 登录后的后续操作，在没有请求退出登录时，不断进行生成试卷和切换类型询问
void Teacher::AfterLogin() {
  MakePaper();
  while (!request_logout_) {
    SelectMode();
    if (request_logout_) {
      return;
    }
    MakePaper();
  }
}

// 输入合法的切换选项或不切换确认后，设置为新的类型并退出该函数
// 否则不断提示输入切换类型，直到输入合法的选项
void Teacher::SelectMode() {
  string answer;
  bool confirmed = false;
  while (!confirmed) {
    printf(
        "\n是否需要切换试卷类型，若需要，请输入“切换为XX”，XX为小学、初中和高中"
        "三"
        "个选项中的一个；否则，请输入“否”：\n");
    answer = GetString();
    if (answer == "切换为小学") {
      mode_ = 'X';
      mode_name_ = "小学";
      confirmed = true;
    } else if (answer == "切换为初中") {
      mode_ = 'C';
      mode_name_ = "初中";
      confirmed = true;
    } else if (answer == "切换为高中") {
      mode_ = 'G';
      mode_name_ = "高中";
      confirmed = true;
    } else if (answer == "否") {
      confirmed = true;
    } else {
      printf("\n\033[0;33;40m请输入小学、初中和高中三个选项中的一个\033[0m\n");
    }
  }
  printf("\n");
}

// 根据输入的题目数量和试卷类型，调用MakeQuestion函数相应次数，并进行题目查重
// 题目无重复则加入该试卷题目组和所有已出题目中，出完为止
void Teacher::MakePaper() {
  int i, question_num;
  bool confirmed = false;
  string temp_question;
  vector<string> questions;
  printf("\n--------当前选择为%s出题--------\n\n", mode_name_.c_str());
  while (!confirmed) {
    printf(
        "\n准备生成%s数学题目，请输入生成题目数量（输入-"
        "1将退出当前用户，重新登录）：",
        mode_name_.c_str());
    scanf("%d", &question_num);
    if (question_num == -1) {
      request_logout_ = true;
      return;
    } else if (question_num < 10 || question_num > 30) {
      printf(
          "\n\033[0;33;40m输入的题目数量有误，请输入一个10-30间的数！\033["
          "0m\n");
    } else {
      confirmed = true;
    }
  }
  for (i = 0; i < question_num; i++) {
    temp_question = MakeQuestion(mode_);
    if (CheckRepeat(temp_question)) {
      i--;
    } else {
      questions.push_back(temp_question);
      all_question_.push_back(temp_question);
    }
  }
  WritePaperIntoFile(name_, questions);
  printf("\n\033[0;32;40m生成成功！请在账号名文件夹中查看试卷！\033[0m\n");
}

// 退出登录前，将各成员变量重置为未登录的初始状态
void Teacher::LogoutInit() {
  already_login_ = false;
  request_logout_ = false;
  mode_ = 'N';
  name_ = "";
  mode_name_ = "NULL";
}

// 题目查重通过查找该题目是否已存在于所有已出题目中实现
// 若有重复则返回true
bool Teacher::CheckRepeat(string question) {
  vector<string>::iterator iter;
  iter = find(all_question_.begin(), all_question_.end(), question);
  return iter != all_question_.end();
}

// 调试函数，打印已出的题目数和全部题目
void Teacher::Test() {
  printf("%d\n", all_question_.size());
  for (int i = 0; i < all_question_.size(); i++) {
    printf("%s\n", all_question_[i].c_str());
  }
}
}  // namespace teacher
}  // namespace paper
